//Copyright 2011 New York City 3D Community

//This file is part of New York City 3D.

//New York City 3D is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//New York City 3D is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with New York City 3D.  If not, see <http://www.gnu.org/licenses/>.

package nyc3d.street;

import com.jme3.asset.AssetManager;
import com.jme3.light.PointLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Cylinder;

/**
 *
 * @author Joseph
 */
public class TrafficLightSide {
    TrafficLight parentTrafficLight;
    Geometry redCircle, yellowCircle, greenCircle, currentCircle;
    Node parentNode;
    PointLight greenPointLight, yellowPointLight, redPointLight, currentPointLight;
    long greenTime, yellowTime, time;
    public enum Type{MAIN, SECONDARY};
    private Type type;

    public TrafficLightSide(Type type, String trafficLightName, AssetManager assetManager, Vector3f translation, Vector3f rotation, TrafficLight parentTrafficLight, Node parentNode){
        this.type = type;
        this.parentNode = parentNode;

        this.parentTrafficLight = parentTrafficLight;
        redCircle = new Geometry(trafficLightName + " Red Circle", new Cylinder(12, 12, .29f, .1f, true));
        Material matRedCircle = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        matRedCircle.setColor("Color", ColorRGBA.Red);
        redCircle.setMaterial(matRedCircle);
        redCircle.setLocalTranslation(translation);

        yellowCircle = new Geometry(trafficLightName + " Yellow Circle", new Cylinder(12, 12, .29f, .1f, true));
        Material matYellowCircle = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        matYellowCircle.setColor("Color", ColorRGBA.Yellow);
        yellowCircle.setMaterial(matYellowCircle);
        yellowCircle.setLocalTranslation(redCircle.getLocalTranslation().add(0, -.9f, 0));

        greenCircle = new Geometry(trafficLightName + " Green Circle", new Cylinder(12, 12, .29f, .1f, true));
        Material matGreenCircle = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        matGreenCircle.setColor("Color", ColorRGBA.Green);
        greenCircle.setMaterial(matGreenCircle);
        greenCircle.setLocalTranslation(redCircle.getLocalTranslation().add(0, -1.8f, 0));

        if (type.equals(Type.MAIN)){
            redCircle.rotate(rotation.x, rotation.y, rotation.z);
            yellowCircle.rotate(rotation.x, rotation.y, rotation.z);
            greenCircle.rotate(rotation.x, rotation.y, rotation.z);
        } else{
            redCircle.rotate(rotation.x, rotation.y - (FastMath.PI / 2f), rotation.z);
            yellowCircle.rotate(rotation.x, rotation.y - (FastMath.PI / 2f), rotation.z);
            greenCircle.rotate(rotation.x, rotation.y - (FastMath.PI / 2f), rotation.z);
        }

        redPointLight = new PointLight();
        redPointLight.setPosition(redCircle.getWorldTranslation());
        redPointLight.setColor(ColorRGBA.Red.clone().multLocal(.05f));
        redPointLight.setRadius(20);

        yellowPointLight = new PointLight();
        yellowPointLight.setPosition(yellowCircle.getWorldTranslation());
        yellowPointLight.setColor(ColorRGBA.Yellow.clone().multLocal(.05f));
        yellowPointLight.setRadius(20);

        greenPointLight = new PointLight();
        greenPointLight.setPosition(greenCircle.getWorldTranslation());
        greenPointLight.setColor(ColorRGBA.Green.clone().multLocal(.05f));
        greenPointLight.setRadius(20);

        if (type.equals(Type.MAIN)){
            currentPointLight = greenPointLight;
            currentCircle = greenCircle;
            
        }
        else{
            currentPointLight = redPointLight;
            currentCircle = redCircle;
        }

        parentTrafficLight.getParentNode().attachChild(currentCircle);
        parentNode.addLight(currentPointLight);
        
        if (type.equals(Type.MAIN))
            greenTime = 30000;
        else
            greenTime = 20000;
        
        yellowTime = 2500;
        time = System.currentTimeMillis();
    }

    public void update(float tpf){

        if (currentCircle.equals(greenCircle)){
            if (System.currentTimeMillis() - time >= greenTime){
               parentTrafficLight.getParentNode().detachChild(currentCircle);
               parentNode.removeLight(currentPointLight);
               currentCircle = yellowCircle;
               currentPointLight = yellowPointLight;
               parentTrafficLight.getParentNode().attachChild(currentCircle);
               parentNode.addLight(currentPointLight);
               time = System.currentTimeMillis();

                    for(int i = 0; i < parentTrafficLight.linkedTrafficLights.size(); i++){

                        for(int n = 0; n < parentTrafficLight.linkedTrafficLights.get(i).getSides().length; n++){
                            if (parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getType().equals(type)){
                                parentTrafficLight.linkedTrafficLights.get(i).getParentNode().detachChild(parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getCurrentCircle());
                                parentNode.removeLight(parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getCurrentPointLight());
                                parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].setCurrentCircle(parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getYellowCircle());
                                parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].setCurrentPointLight(parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getYellowPointLight());
                                parentTrafficLight.linkedTrafficLights.get(i).getParentNode().attachChild(parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getCurrentCircle());
                                parentNode.addLight(parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getCurrentPointLight());
                                parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].setTime(System.currentTimeMillis());
                            }

                        }
                    }

                switch(type){
                    case MAIN:
                        for(int i = 0; i < parentTrafficLight.getLinkedWalkSignals().size(); i++){
                                for(int n = 0; n < parentTrafficLight.getLinkedWalkSignals().get(i).getSides().length; n++){
                                    if (parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getType().equals(PedestrianWalkSignalSide.Type.MAIN)){
                                        if (!parentTrafficLight.getLinkedWalkSignals().get(i).getNode().hasChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect())){
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getNode().attachChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect());
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getParentNode().addLight(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentLight());

                                        }
                                    }
                                }
                            }
                        break;

                    case SECONDARY:
                        for(int i = 0; i < parentTrafficLight.getLinkedWalkSignals().size(); i++){
                                for(int n = 0; n < parentTrafficLight.getLinkedWalkSignals().get(i).getSides().length; n++){
                                    if (parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getType().equals(PedestrianWalkSignalSide.Type.SECONDARY)){
                                        if (!parentTrafficLight.getLinkedWalkSignals().get(i).getNode().hasChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect())){
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getNode().attachChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect());
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getParentNode().addLight(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentLight());

                                        }
                                    }
                                }
                            }
                        break;
         }
            }
             else if (greenTime - (System.currentTimeMillis() - time) <= 9000){
                 checkChangeGoToStop();

                 switch(type){
                    case MAIN:
                        for(int i = 0; i < parentTrafficLight.getLinkedWalkSignals().size(); i++){
                                for(int n = 0; n < parentTrafficLight.getLinkedWalkSignals().get(i).getSides().length; n++){
                                    if (parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getType().equals(PedestrianWalkSignalSide.Type.MAIN)){
                                        if (parentTrafficLight.getLinkedWalkSignals().get(i).getNode().hasChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect())){
                                           if (System.currentTimeMillis() - parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getTime() >= parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getBlinkTime()){
                                               parentTrafficLight.getLinkedWalkSignals().get(i).getNode().detachChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect());
                                               parentTrafficLight.getLinkedWalkSignals().get(i).getParentNode().removeLight(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentLight());
                                               parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].setTime(System.currentTimeMillis());
                                           }
                                        }
                                        else{
                                            if (System.currentTimeMillis() - parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getTime() >= parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getWaitTime()){
                                               parentTrafficLight.getLinkedWalkSignals().get(i).getNode().attachChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect());
                                               parentTrafficLight.getLinkedWalkSignals().get(i).getParentNode().addLight(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentLight());
                                               parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].setTime(System.currentTimeMillis());
                                           }
                                        }
                          
                                    }
                                }
                            }
                        break;

                    case SECONDARY:
                        for(int i = 0; i < parentTrafficLight.getLinkedWalkSignals().size(); i++){
                                for(int n = 0; n < parentTrafficLight.getLinkedWalkSignals().get(i).getSides().length; n++){
                                    if (parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getType().equals(PedestrianWalkSignalSide.Type.SECONDARY)){
                                        if (parentTrafficLight.getLinkedWalkSignals().get(i).getNode().hasChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect())){
                                           if (System.currentTimeMillis() - parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getTime() >= parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getBlinkTime()){
                                               parentTrafficLight.getLinkedWalkSignals().get(i).getNode().detachChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect());
                                               parentTrafficLight.getLinkedWalkSignals().get(i).getParentNode().removeLight(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentLight());
                                               parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].setTime(System.currentTimeMillis());
                                           }
                                        }
                                        else{
                                            if (System.currentTimeMillis() - parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getTime() >= parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getWaitTime()){
                                               parentTrafficLight.getLinkedWalkSignals().get(i).getNode().attachChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect());
                                               parentTrafficLight.getLinkedWalkSignals().get(i).getParentNode().addLight(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentLight());
                                               parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].setTime(System.currentTimeMillis());
                                           }
                                        }
                                    }
                                }
                            }
                        break;
         }

             }  
        }

        if (currentCircle.equals(yellowCircle) && System.currentTimeMillis() - time >= yellowTime){
                parentTrafficLight.getParentNode().detachChild(currentCircle);
                parentNode.removeLight(currentPointLight);
                currentCircle = redCircle;
                currentPointLight = redPointLight;
                parentTrafficLight.getParentNode().attachChild(currentCircle);
                parentNode.addLight(currentPointLight);
                time = System.currentTimeMillis();
                parentTrafficLight.triggerRedLight(type);

                

                for(int i = 0; i < parentTrafficLight.linkedTrafficLights.size(); i++){
                    for(int n = 0; n < parentTrafficLight.linkedTrafficLights.get(i).getSides().length; n++){
                        if (parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getType().equals(type)){
                            parentTrafficLight.linkedTrafficLights.get(i).getParentNode().detachChild(parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getCurrentCircle());
                            parentNode.removeLight(parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getCurrentPointLight());
                            parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].setCurrentCircle(parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getRedCircle());
                            parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].setCurrentPointLight(parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getRedPointLight());
                            parentTrafficLight.linkedTrafficLights.get(i).getParentNode().attachChild(parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getCurrentCircle());
                            parentNode.addLight(parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].getCurrentPointLight());
                            parentTrafficLight.linkedTrafficLights.get(i).getSides()[n].setTime(System.currentTimeMillis());
                            parentTrafficLight.linkedTrafficLights.get(i).triggerRedLight(type);
                        }

                    }
                }
  
        }
    }

    private void checkChangeGoToStop(){
        switch(type){
                    case MAIN:
                        for(int i = 0; i < parentTrafficLight.getLinkedWalkSignals().size(); i++){
                                for(int n = 0; n < parentTrafficLight.getLinkedWalkSignals().get(i).getSides().length; n++){
                                    if (parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getType().equals(PedestrianWalkSignalSide.Type.MAIN)){
                                        if (parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect().equals(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getGoRect())){
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getNode().detachChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect());
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getParentNode().removeLight(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentLight());                                            
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].setCurrentRect(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getStopRect());
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].setCurrentLight(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getStopLight());
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getNode().attachChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect());
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getParentNode().addLight(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentLight());
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].setTime(System.currentTimeMillis());
                                        }
                                    }
                                }
                            }
                        break;

                    case SECONDARY:
                        for(int i = 0; i < parentTrafficLight.getLinkedWalkSignals().size(); i++){
                                for(int n = 0; n < parentTrafficLight.getLinkedWalkSignals().get(i).getSides().length; n++){
                                    if (parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getType().equals(PedestrianWalkSignalSide.Type.SECONDARY)){
                                        if (parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect().equals(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getGoRect())){
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getNode().detachChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect());
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getParentNode().removeLight(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentLight());
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].setCurrentRect(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getStopRect());
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].setCurrentLight(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getStopLight());
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getNode().attachChild(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentRect());
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getParentNode().addLight(parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].getCurrentLight());
                                            parentTrafficLight.getLinkedWalkSignals().get(i).getSides()[n].setTime(System.currentTimeMillis());
                                        }
                                    }
                                }
                            }
                        break;
         }
    }

    public Geometry getCurrentCircle() {
        return currentCircle;
    }

    public void setCurrentCircle(Geometry currentCircle) {
        this.currentCircle = currentCircle;
    }

    public PointLight getCurrentPointLight() {
        return currentPointLight;
    }

    public void setCurrentPointLight(PointLight currentPointLight) {
        this.currentPointLight = currentPointLight;
    }

    public Geometry getGreenCircle() {
        return greenCircle;
    }

    public void setGreenCircle(Geometry greenCircle) {
        this.greenCircle = greenCircle;
    }

    public PointLight getGreenPointLight() {
        return greenPointLight;
    }

    public void setGreenPointLight(PointLight greenPointLight) {
        this.greenPointLight = greenPointLight;
    }

    public long getGreenTime() {
        return greenTime;
    }

    public void setGreenTime(long greenTime) {
        this.greenTime = greenTime;
    }

    public Geometry getRedCircle() {
        return redCircle;
    }

    public void setRedCircle(Geometry redCircle) {
        this.redCircle = redCircle;
    }

    public PointLight getRedPointLight() {
        return redPointLight;
    }

    public void setRedPointLight(PointLight redPointLight) {
        this.redPointLight = redPointLight;
    }

    public long getTime() {
        return time;
    }

    public void setTime(long time) {
        this.time = time;
    }

    public Type getType() {
        return type;
    }

    public void setType(Type type) {
        this.type = type;
    }

    public Geometry getYellowCircle() {
        return yellowCircle;
    }

    public void setYellowCircle(Geometry yellowCircle) {
        this.yellowCircle = yellowCircle;
    }

    public PointLight getYellowPointLight() {
        return yellowPointLight;
    }

    public void setYellowPointLight(PointLight yellowPointLight) {
        this.yellowPointLight = yellowPointLight;
    }

    public long getYellowTime() {
        return yellowTime;
    }

    public void setYellowTime(long yellowTime) {
        this.yellowTime = yellowTime;
    }

    public TrafficLight getParentTrafficLight() {
        return parentTrafficLight;
    }

    public void setParentTrafficLight(TrafficLight parentTrafficLight) {
        this.parentTrafficLight = parentTrafficLight;
    }
}